package com.yupi.lkzyoj.judge.codeSandBox.impl;

import cn.hutool.core.io.FileUtil;
import com.yupi.lkzyoj.judge.codeSandBox.ICodeSandBox;
import com.yupi.lkzyoj.judge.codeSandBox.model.ExecuteCodeRequest;
import com.yupi.lkzyoj.judge.codeSandBox.model.ExecuteCodeResponse;
import com.yupi.lkzyoj.judge.codeSandBox.model.ProcessResponse;
import com.yupi.lkzyoj.model.dto.questionsubmit.JudgeInfo;
import com.yupi.lkzyoj.utils.RunProcessUtil;
import org.springframework.util.StopWatch;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

/**
 * 示例代码箱沙箱
 * 前期开发、测试沙箱
 *
 * @author Wei
 * @date 2024/05/28
 */
public class ExampleCodeBoxSandBoxImpl implements ICodeSandBox {

    private static Boolean isTimeout = false;

    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        //System.out.println("*****示例代码沙箱*****");
        //System.out.println("提交参数:::" + executeCodeRequest.toString());
        //return ExecuteCodeResponse.builder().message("运行成功").judgeInfo(new JudgeInfo("运行成功!", 900L, 600L)).outputList(Arrays.asList("翻转链表")).status(200).build();
        //

        System.out.println("*****示例代码沙箱*****");
        if (executeCodeRequest == null)
            return ExecuteCodeResponse.builder().message("参数错误运行失败").judgeInfo(new JudgeInfo()).outputList(null).status(502).build();
        System.out.println("提交参数:::" + executeCodeRequest);

        ExecuteCodeResponse res = new ExecuteCodeResponse();
        res.setMessage("所有用例正常执行");
        res.setStatus(200);
        res.setJudgeInfo(null);


        //region 1. 将用户提交的代码保存为文件
        // - 获取到代码目录
        String codeDirPath = System.getProperty("user.dir") + File.separator + "tempCodeDir";
        // - 当前进程UUID
        UUID uuid = UUID.randomUUID();
        // - 创建对应文件夹
        String curDir = codeDirPath + File.separator + uuid;
        File mkdir = FileUtil.mkdir(curDir);
        // - 创建对应的Main.Java文件
        File codeFile = FileUtil.newFile(curDir + File.separator + "Main.java");
        // - 向当前线程的Main.java文件中写入代码
        FileUtil.writeBytes(executeCodeRequest.getCode().getBytes(), codeFile);
        //endregion

        //region 2. 通过 javac 将源文件编译为字节码文件
        String absolutePath = codeFile.getAbsolutePath();
        try {
            System.out.println("javac -encoding utf-8 " + absolutePath);
            // - 执行命令
            Process compileProcess = Runtime.getRuntime().exec("javac -encoding utf-8 " + absolutePath);
            ProcessResponse processResponse = RunProcessUtil.runProcess(compileProcess);
            if (processResponse.getExitValue().equals(0)) {
                System.out.println("编译成功 success:");
                System.out.println(processResponse.getSuccessMsg());
            } else {
                System.out.println("编译失败 default:");
                System.out.println(processResponse.getErrorMsg());
                res.setMessage("编译错误");
                res.setStatus(300);
                return res;
            }

        } catch (Exception e) {
            e.printStackTrace();
            //    编译错误直接返回
            res.setMessage("编译错误");
            res.setStatus(300);
            return res;
        }
        //endregion

        // region 3. 执行字节码文件运行程序(多次执行输入不同的输入用例) 4. 收集整理输出结果
        List<String> outputList = new ArrayList<>();
        Long maxTime = new Long(0);

        for (String args : executeCodeRequest.getInputList()) {
            try {
                StopWatch stopWatch = new StopWatch();
                stopWatch.start();
                // 乱码参考文章 https://www.cnblogs.com/Chary/articles/18072180
                //Process process = Runtime.getRuntime().exec("java -Dfile.encoding=UTF-8 -cp " + curDir + " Main 6 8");
                Process process = Runtime.getRuntime().exec("java -Dfile.encoding=UTF-8 -cp " + curDir + " Main " + args);
                isTimeout = false;
                ProcessResponse processResponse = new ProcessResponse();
                new Thread(() -> {
                    try {
                        System.out.println("开始监视");
                        Thread.sleep(5000L);
                        if (process != null && process.isAlive()) {
                            process.destroy();
                            System.out.println("超时结束进程");
                            isTimeout = true;
                        }
                        //    超时返回
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }).start();
                processResponse = RunProcessUtil.runProcess(process);


                //     交互式运行方式
                //ProcessResponse processResponse = RunProcessUtil.runInteractProcess(process, "2\n6");

                // 处理结果
                if (processResponse.getExitValue().equals(0)) {
                    System.out.println("运行成功 success:");
                    System.out.println(processResponse.getSuccessMsg());
                    // 4. 收集整理输出结果
                    outputList.add(processResponse.getSuccessMsg());

                    // 计算单次测试用例最大时长
                    stopWatch.stop();
                    maxTime = Math.max(maxTime, stopWatch.getLastTaskTimeMillis());
                } else {
                    System.out.println("运行失败 default:");
                    System.out.println(processResponse.getErrorMsg());
                    // 4. 收集整理输出结果
                    // 如果有运行错误的用例  直接返回本次用例的提示信息和本次用例的输入用例
                    res.setStatus(301);
                    res.setMessage("参数:" + args + "\n错误信息:" + processResponse.getErrorMsg());
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        res.setOutputList(outputList);

        //endregion

        // 5. 文件清除
        // 删除当前线程的文件和文件夹
        //System.out.println("是否成功删除:" + FileUtil.del(curDir));

        if (res.getStatus().equals(200)) {
            //    样例全部通过设置JudgeInfo信息
            JudgeInfo judgeInfo = new JudgeInfo();
            judgeInfo.setMemory(200L);
            judgeInfo.setTime(maxTime);
            res.setJudgeInfo(judgeInfo);
        }
        return res;
    }
}
